# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: inkyu

import logging

from collections import namedtuple

from coin.exchange.kr_rest.product_holders import get_holder_from_product
from coin.strategy.cheese.util.vwap import VwapCalculator

PassivePrices = namedtuple('PassivePrices',
                           ['askp', 'bidp', 'askp_pull', 'bidp_pull', 'edge_buy', 'edge_sell'])


class SidecarL3Pricer(object):
  def __init__(self, *, product, vwap_long_sec, vwap_short_sec=10):
    self.product = product
    self._logger = logging.getLogger(__name__)

    self._tick = get_holder_from_product(product).get_tick()
    self.timestamp = None
    self.book_ready = False
    self.vwap_ready = False

    self.ask0 = 0.
    self.bid0 = 0.
    self.askt = 0.
    self.bidt = 0.
    self.midp = 0.
    self.midt = 0.
    self.ask0_minus_one = 0.
    self.bid0_plus_one = 0.

    self._book = None
    self._true_book_price = 0
    self._true_book_qty = None

    self.vwap_long = None
    self.vwap_short = None
    self._vwap_long = VwapCalculator(vwap_long_sec)
    self._vwap_short = VwapCalculator(vwap_short_sec)
    self._book_count = 0

  def on_book(self, book, available):
    self._book = book
    self.timestamp = book.timestamp

    if not (book.has_ask() and book.has_bid()):
      self.reset()
      return

    self.ask0 = book.ask0().price
    self.bid0 = book.bid0().price
    self.ask0_qty = book.ask0().qty
    self.bid0_qty = book.bid0().qty
    self.askt = self._book_askt_func(book)
    self.bidt = self._book_bidt_func(book)
    self.midp = (self.ask0 + self.bid0) / 2
    self.midt = (self.askt + self.bidt) / 2

    assert self.askt >= self.ask0 >= self.midp >= self.bid0 >= self.bidt, (self.askt,
                                                                           self.ask0,
                                                                           self.midp,
                                                                           self.bid0,
                                                                           self.bidt)

    # Cut-in-line
    self.ask0_minus_one = self._tick.get_prev_price(self.ask0)
    self.bid0_plus_one = self._tick.get_next_price(self.bid0)

    # Vwap
    self.vwap_long = self._vwap_long.get()
    self.vwap_short = self._vwap_short.get()
    self._book_count += 1

    if not self.vwap_ready and self.vwap_long is not None and self.vwap_short is not None:
      self._logger.info('vwap ready: %s', self.product)
      self.vwap_ready = True
    if not self.book_ready:
      self._logger.info('book ready: %s', self.product)
      self.book_ready = True

  def on_trade(self, trade, available):
    volume = trade.qty * trade.price
    self._vwap_long.set(volume=volume, qty=trade.qty, timestamp=trade.timestamp)
    self._vwap_short.set(volume=volume, qty=trade.qty, timestamp=trade.timestamp)
    # self._logger.warn('%s %s', trade.symbol, self._vwap_long.get())

  @property
  def true_book_price(self):
    return self._true_book_price

  def set_true_book_price(self, true_book_price):
    self._true_book_price = true_book_price

  def set_true_book_qty(self, true_book_qty):
    self._true_book_qty = true_book_qty

  def _book_askt_func(self, book):
    if self._true_book_qty is not None:
      return book.get_notional_asks_by_qty(self._true_book_qty).entries[-1].price
    else:
      res = book.get_notional_asks_by_amt(self._true_book_price)
      if res is None:
        print(book.product)
      return res.entries[-1].price

  def _book_bidt_func(self, book):
    if self._true_book_qty is not None:
      return book.get_notional_bids_by_qty(self._true_book_qty).entries[-1].price
    else:
      return book.get_notional_bids_by_amt(self._true_book_price).entries[-1].price

  def reset(self):
    self.book_ready = False
    self.vwap_ready = False

  def correct_ask_passive(self, ask_orig, cut_in_line=False):
    askp = self._tick.ceil(ask_orig)
    if cut_in_line:
      askp = max(askp, self.ask0_minus_one, self.bid0_plus_one)
    return askp

  def correct_bid_passive(self, bid_orig, cut_in_line=False):
    bidp = self._tick.floor(bid_orig)
    if cut_in_line:
      bidp = min(bidp, self.ask0_minus_one, self.bid0_plus_one)
    return bidp

  def get_lower_tick(self, price):
    return self._tick.floor(self._tick.get_prev_price(price))

  def get_higher_tick(self, price):
    return self._tick.ceil(self._tick.get_next_price(price))

  def correct_ask_aggressive(self, ask_orig):
    return self.get_lower_tick(ask_orig)

  def correct_bid_aggressive(self, bid_orig):
    return self.get_higher_tick(bid_orig)
